
package com.hyst.common;

import java.util.Properties;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.MappedStatement.Builder;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import com.hyst.annotation.PowerColumn;

/**
 * @Description:mybatis拦截器，拦截进行权限验证
 * @Date：2018年7月18日 @author： DongY
 * @version ：1.0 1.[2018年7月18日]CreatBy：dy
 * @注解说明：@Signature签名指向要拦截StatementHandler这个接口类下的prepare方法， setProperties方法:该方法通过设置属性，将核心配置文件configuration.xml文件中对拦截器的配置项下的属性获取过来，便于在拦截器中使用。
 *                                                          plugin方法：该方法用来协商，达成协议，把代理权给普通的业务员this，传进wrap方法实现的源码去做代理，没有获取代理权的代理人在这个地方就会停下，不会向下走了，获取代理权的代理人可以去做拦截代理。
 *                                                          intercept方法：则是获取拦截对象下的要拦截的东西，然后对其加以改编，添加自己的行为，按照条件进行改编拦截对象，然后通过源码下的反射invocation来调用被拦截的方法，让原本被拦截的方法继续执行（invocation.proceed()）。
 */
@Intercepts({
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }), })
public class MybatisInterceptor implements Interceptor {

	@Override
	public Object intercept(final Invocation invocation) throws Throwable {
		invocation.getTarget();
		final Object[] args = invocation.getArgs();
		final MappedStatement ms = (MappedStatement) args[0];
		final Object parameterObj = args[1];
		final Class<?> returnClazz = parameterObj.getClass();
		final PowerColumn ann = returnClazz.getAnnotation(com.hyst.annotation.PowerColumn.class);
		final String methodName = ms.getId();
		System.err.println(methodName);
		if (ann != null && isValidate(methodName)) {
			// 继续执行
			System.out.println("");
			BoundSql boundSql;
			// 由于逻辑关系，只会进入一次
			if (args.length == 4) {
				// 4 个参数时
				boundSql = ms.getBoundSql(parameterObj);
			} else {
				// 6 个参数时
				boundSql = (BoundSql) args[5];
			}
			String sql = boundSql.getSql();
			System.err.println("获取到原始sql\t：" + sql);
			sql = casesql(sql, ann.value());
			System.err.println("修改后的sql\t：" + sql);
			final BoundSql newBoundsql = new BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(),
					boundSql.getParameterObject());
			copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundsql));
		}

		return invocation.proceed();
	}

	private String casesql(String sql, final String column) {
		sql = sql.toLowerCase();
		final String presql = sql.substring(0, sql.lastIndexOf("from") + 4);
		sql = sql.substring(sql.lastIndexOf("from") + 4);
		if (sql.indexOf("where") > 0) {
			sql = sql.replace("where ", "where " + column + " in( " + getPowers() + ") and ");
		} else {
			sql = sql.trim();
			// if(sql.startsWith(" ")){
			// sql=sql.substring(" ");
			// }
			if (sql.contains(" ")) {

				final String table = sql.substring(0, sql.indexOf(" "));
				String a = sql.substring(sql.indexOf(" "));
				a = a == null ? "" : a;
				sql = " " + table + " where " + column + " in( " + getPowers() + ") " + a;
			} else {
				sql += " where " + column + " in( " + getPowers() + ") ";
			}
		}
		return presql + " " + sql;
	}

	/**
	 * @Description:
	 * @return String
	 */
	private String getPowers() {
		// final List<String> list = pw.getpowers();
		final String string = "'1','2'";
		// for (int i = 0; i < list.size(); i++) {
		// if (i > 0) {
		// string += "," + list.get(i);
		// } else {
		// string += list.get(i);
		// }
		// }
		return string;
	}

	@Override
	public Object plugin(final Object target) {
		// TODO Auto-generated method stub
		return Plugin.wrap(target, this);// 这些莫认调用
	}

	@Override
	public void setProperties(final Properties properties) {
		// TODO Auto-generated method stub

	}

	private boolean isValidate(final String name) {
		return name.endsWith(".select") || name.endsWith("selectAll");
		// return "select".equals(name) || "selectAll".equals(name);
	}

	private MappedStatement copyFromMappedStatement(final MappedStatement ms, final SqlSource newSqlSource) {
		final Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource,
				ms.getSqlCommandType());

		builder.resource(ms.getResource());
		builder.fetchSize(ms.getFetchSize());
		builder.statementType(ms.getStatementType());
		builder.keyGenerator(ms.getKeyGenerator());
		if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) {
			builder.keyProperty(ms.getKeyProperties()[0]);
		}
		// setStatementTimeout()
		builder.timeout(ms.getTimeout());

		// setStatementResultMap()
		builder.parameterMap(ms.getParameterMap());

		// setStatementResultMap()
		builder.resultMaps(ms.getResultMaps());
		builder.resultSetType(ms.getResultSetType());

		// setStatementCache()
		builder.cache(ms.getCache());
		builder.flushCacheRequired(ms.isFlushCacheRequired());
		builder.useCache(ms.isUseCache());

		return builder.build();
	}

	public static class BoundSqlSqlSource implements SqlSource {

		private BoundSql boundSql;

		public BoundSqlSqlSource(final BoundSql boundSql) {
			this.boundSql = boundSql;
		}

		@Override
		public BoundSql getBoundSql(final Object parameterObject) {
			return boundSql;
		}

	}

}
